//需要安装easyx的图形库才可以编译运行 https://www.easyx.cn/downloads/
#define _CRT_SECURE_NO_WARNINGS
#include <graphics.h>
#include <conio.h>
#include <stdio.h>
#include <windows.h>
#define GRID_LENGTH  10 * 3

static int presentscore = 0;       //记录当前的得分
static int beforescore = 0;     //记录之前的最好得分
void startup();     //初始化游戏开始界面
int showsquare(int xl, int yl, int revl, int x, int y, int rev, int color, int tag);       //在(x,y)画出方块 rev表示方块的旋转状态（1表示初始状态，2表示转动90度后，3表示转动180度后，4表示转动270度后）color 表示要画方块的颜色 tag 表示方块的种类。(xl,yl)是未画之前的坐标，real是之前的旋转状态，把它们传进去是为了画出图形后（经过是否能画的判断）可以擦去旧图形，从而产生动态效果。函数返回0或1,0表示在（x,y）画rev状态的方块不可行，1表示可行
void updatewithoutinput(int* x, int* y);     //与用户无关的更新，会对(x,y)做出修改
void updatewithinput(int *xl, int *yl, int *revl, int* x, int* y, int* rev, int color, int tag);       //与用户有关的更新，需要把之前状态也传进去，因为其中会有showsquare()函数操作 
int square0(int x, int y, int rev, int color);     //首先判断是否可以画出，如可以画出"T"，返回1,否则不画返回0
int square1(int x, int y, int rev, int color);     //"L"同上
int square2(int x, int y, int rev, int color);     //"1"同上
int square3(int x, int y, int rev, int color);     //"田"同上
int square4(int x, int y, int rev, int color);     //"Z"同上
int finddelete(int *del);       //判断是否满行，在数组del[]中记录，若有满行则实施消行（del[i] = -1表示第i行需要消去，del[i] = n,n>=0 n>0表示消去后第i行需要往下移动的行数，0表示不用移动）。函数返回0或1，1表示有满行，0表示无满行
int moveline(int *del);       //根据del[]中记录的情况向下移动行。函数返回此次总共消去的行数
int showselection();     //在方块到顶后，画出是否继续游戏的选择窗口。函数返回0或1，0表示直接退出程序，1表示重新开局
int gameover();     //判断方块是否到顶，函数返回0或1
void myfloodfill(int x, int y, int color);        //在以（x,y）为中心的方块内填充指定的颜色
int(*square[5])(int, int, int, int) = { square0,square1,square2,square3,square4 };      //放函数指针的数组，随机产生0到4的数字后就可以随机调用相应的函数，产生方块随机掉落的效果


int main() {
	int again = 1;
	while (again) {
		startup();
		int i = 0, j = 0;       //j = 0, 1, 2, 3, 4
		int colorarray[3] = { RED,YELLOW,GREEN };
		while (1) {
			int x = 55 * 3, y = 35 * 3, rev = 1, xl, yl, revl;
			j = rand() % 5;     //rand()%5 -> j
			if (i == 3) i = 0;
			int color = colorarray[i];
			if (gameover() == 0) break;
			(*square[j])(x, y, rev, color);
			i++;
			while (1) {
				xl = x; yl = y, revl = rev;
				updatewithinput(&xl, &yl, &revl, &x, &y, &rev, color, j);
				updatewithoutinput(&x, &y);
				int flag = 1;
				flag = showsquare(xl, yl, revl, x, y, rev, color, j);
				Sleep(400);
				if (flag == 0) {
					int del[16];
					for (int i = 0; i<16; i++) del[i] = 0;
					if (finddelete(del)) {
						int q = moveline(del);
						wchar_t s[] = L"0000000000000";
						wsprintf(s, L"%d", q * 10 + presentscore);
						presentscore = q * 10 + presentscore;
						outtextxy(125 * 3, 110 * 3, s);
					}
					break;
				}
			}
		}
		if (presentscore>beforescore) {
			FILE *fp;
			fp = fopen("in.txt", "w+");
			fprintf(fp, "%d", presentscore);
			fclose(fp);
		}
		again = showselection();
	}
	closegraph();
	return 0;
}

int gameover() {
	int flag = 1;
	for (int t = 1; t <= 10; t++) {
		if (getpixel(t * GRID_LENGTH + 5 * 3, 45 * 3) != LIGHTBLUE) flag = 0;
	}
	return flag;
}
void myfloodfill(int x, int y, int color) {
	setfillcolor(color);
	solidrectangle(x - 5 * 3 + 1, y - 5 * 3 + 1, x + 5 * 3 - 1, y + 5 * 3 - 1);
}
void startup() {
	initgraph(160 * 3, 190 * 3);
	setbkcolor(LIGHTBLUE);
	cleardevice();
	setcolor(LIGHTGRAY);
	line(GRID_LENGTH, 20 * 3, 110 * 3, 20 * 3);
	for (int i = 2; i <= 15; i++) {
		line(GRID_LENGTH, i * GRID_LENGTH + 20 * 3, 110 * 3, i * GRID_LENGTH + 20 * 3);
	}
	for (int i = 1; i <= 11; i++) {
		line(10 * i * 3, 20 * 3, 10 * i * 3, 170 * 3);
	}
	outtextxy(GRID_LENGTH, 175 * 3, L"Press any key to start");
	outtextxy(GRID_LENGTH, 180 * 3, L"Down:s  Left:a  Right:d  Turn:k");
	FILE *fp;
	wchar_t s[] = L"00000000000000000000";
	fp = fopen("in.txt", "a+");
	fscanf(fp, "%d", &beforescore);
	fclose(fp);
	wsprintf(s, L"%d", beforescore);
	outtextxy(125 * 3, 60 * 3, L"Best score");
	outtextxy(125 * 3, 70 * 3, s);
	outtextxy(125 * 3, 100 * 3, L"Score");
	outtextxy(125 * 3, 110 * 3, L"0");
	_getch();       //begin
}
int showsquare(int xl, int yl, int revl, int x, int y, int rev, int color, int tag) {
	int flag = 1;
	(*square[tag])(xl, yl, revl, LIGHTBLUE);
	flag = (*square[tag])(x, y, rev, color);
	if (flag == 0) {
		(*square[tag])(xl, yl, revl, color);     //补上不应该擦去的图形
	}
	return flag;
}
int candraw(int x, int y) {
	if (getpixel(x, y) == LIGHTBLUE && x >= 15 * 3 && x <= 105 * 3 && y >= 25 * 3 && y <= 165 * 3)
		return 1;
	else return 0;
}
int square0(int x, int y, int rev, int color) {
	int flag = 0;
	if (rev == 1) {
		if ((candraw(x, y) && candraw(x - GRID_LENGTH, y) && candraw(x + GRID_LENGTH, y) && candraw(x, y - GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x - GRID_LENGTH, y, color); myfloodfill(x + GRID_LENGTH, y, color); flag = 1;
		}
	}
	else if (rev == 2) {
		if ((candraw(x, y) && candraw(x, y - GRID_LENGTH) && candraw(x, y + GRID_LENGTH) && candraw(x + GRID_LENGTH, y)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x, y + GRID_LENGTH, color); myfloodfill(x + GRID_LENGTH, y, color); flag = 1;
		}
	}
	else if (rev == 3) {
		if ((candraw(x, y) && candraw(x, y + GRID_LENGTH) && candraw(x - GRID_LENGTH, y) && candraw(x + GRID_LENGTH, y)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x - GRID_LENGTH, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x, y + GRID_LENGTH, color);	flag = 1;
		}
	}
	else {
		if ((candraw(x, y) && candraw(x - GRID_LENGTH, y) && candraw(x, y - GRID_LENGTH) && candraw(x, y + GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x - GRID_LENGTH, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x, y + GRID_LENGTH, color); flag = 1;
		}
	}
	return flag;
}
int square1(int x, int y, int rev, int color) {
	int flag = 0;
	if (rev == 1) {
		if ((candraw(x, y) && candraw(x + GRID_LENGTH, y) && candraw(x + 20 * 3, y) && candraw(x, y - GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x + 20 * 3, y, color); myfloodfill(x, y - GRID_LENGTH, color); flag = 1;
		}
	}
	else if (rev == 2) {
		if ((candraw(x, y) && candraw(x + GRID_LENGTH, y) && candraw(x, y + GRID_LENGTH) && candraw(x, y + 20 * 3)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x, y + GRID_LENGTH, color); myfloodfill(x, y + 20 * 3, color); flag = 1;
		}
	}
	else if (rev == 3) {
		if ((candraw(x, y) && candraw(x, y + GRID_LENGTH) && candraw(x - GRID_LENGTH, y) && candraw(x - 20 * 3, y)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x, y + GRID_LENGTH, color); myfloodfill(x - GRID_LENGTH, y, color); myfloodfill(x - 20 * 3, y, color);	flag = 1;
		}
	}
	else {
		if ((candraw(x, y) && candraw(x - GRID_LENGTH, y) && candraw(x, y - GRID_LENGTH) && candraw(x, y - 20 * 3)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x - GRID_LENGTH, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x, y - 20 * 3, color); flag = 1;
		}
	}
	return flag;
}
int square2(int x, int y, int rev, int color) {
	int flag = 0;
	if (rev == 1) {
		if ((candraw(x, y) && candraw(x, y - GRID_LENGTH) && candraw(x, y + GRID_LENGTH) && candraw(x, y + 20 * 3)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x, y + GRID_LENGTH, color); myfloodfill(x, y + 20 * 3, color); flag = 1;
		}
	}
	else if (rev == 2) {
		if ((candraw(x, y) && candraw(x + GRID_LENGTH, y) && candraw(x - GRID_LENGTH, y) && candraw(x - 20 * 3, y)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x - GRID_LENGTH, y, color); myfloodfill(x - 20 * 3, y, color); flag = 1;
		}
	}
	else if (rev == 3) {
		if ((candraw(x, y) && candraw(x, y + GRID_LENGTH) && candraw(x, y - GRID_LENGTH) && candraw(x, y - 20 * 3)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x, y + GRID_LENGTH, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x, y - 20 * 3, color);	flag = 1;
		}
	}
	else {
		if ((candraw(x, y) && candraw(x - GRID_LENGTH, y) && candraw(x + GRID_LENGTH, y) && candraw(x + 20 * 3, y)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x - GRID_LENGTH, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x + 20 * 3, y, color); flag = 1;
		}
	}
	return flag;
}
int square3(int x, int y, int rev, int color) {
	int flag = 0;
	if (rev == 1) {
		if ((candraw(x, y) && candraw(x + GRID_LENGTH, y) && candraw(x, y - GRID_LENGTH) && candraw(x + GRID_LENGTH, y - GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x + GRID_LENGTH, y - GRID_LENGTH, color); flag = 1;
		}
	}
	return flag;
}
int square4(int x, int y, int rev, int color) {
	int flag = 0;
	if (rev == 1) {
		if ((candraw(x, y) && candraw(x, y - GRID_LENGTH) && candraw(x + GRID_LENGTH, y) && candraw(x + GRID_LENGTH, y + GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x + GRID_LENGTH, y + GRID_LENGTH, color); flag = 1;
		}
	}
	else if (rev == 2) {
		if ((candraw(x, y) && candraw(x + GRID_LENGTH, y) && candraw(x, y + GRID_LENGTH) && candraw(x - GRID_LENGTH, y + GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x, y + GRID_LENGTH, color); myfloodfill(x - GRID_LENGTH, y + GRID_LENGTH, color); flag = 1;
		}
	}
	else if (rev == 3) {
		if ((candraw(x, y) && candraw(x, y - GRID_LENGTH) && candraw(x + GRID_LENGTH, y) && candraw(x + GRID_LENGTH, y + GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x, y - GRID_LENGTH, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x + GRID_LENGTH, y + GRID_LENGTH, color);	flag = 1;
		}
	}
	else {
		if ((candraw(x, y) && candraw(x + GRID_LENGTH, y) && candraw(x, y + GRID_LENGTH) && candraw(x - GRID_LENGTH, y + GRID_LENGTH)) || (color == LIGHTBLUE)) {
			myfloodfill(x, y, color); myfloodfill(x + GRID_LENGTH, y, color); myfloodfill(x, y + GRID_LENGTH, color); myfloodfill(x - GRID_LENGTH, y + GRID_LENGTH, color); flag = 1;
		}
	}
	return flag;
}
void updatewithoutinput(int* x, int* y) {
	*y = *y + GRID_LENGTH;
}
void updatewithinput(int *xl, int *yl, int* revl, int* x, int* y, int* rev, int color, int tag) {
	char input;
	while (_kbhit()) {
		input = _getch();
		if (input == 'k' || input == 'K') {
			if (tag != 3) {
				*rev = *rev + 1;
				if (*rev == 5) *rev = 1;
				if (showsquare(*xl, *yl, *revl, *x, *y, *rev, color, tag)) {
					*xl = *x; *yl = *y; *revl = *rev;
				}
				else *rev = *rev - 1;
			}
		}
		else if (input == 'a' || input == 'A') {
			*x = *x - GRID_LENGTH;
			if (showsquare(*xl, *yl, *revl, *x, *y, *rev, color, tag)) {
				*xl = *x; *yl = *y; *revl = *rev;
			}
			else *x = *x + GRID_LENGTH;
		}
		else if (input == 'd' || input == 'D') {
			*x = *x + GRID_LENGTH;
			if (showsquare(*xl, *yl, *revl, *x, *y, *rev, color, tag)) {
				*xl = *x; *yl = *y; *revl = *rev;
			}
			else *x = *x - GRID_LENGTH;
		}
		else if (input == 's' || input == 'S') {
			*y = *y + GRID_LENGTH;
			if (showsquare(*xl, *yl, *revl, *x, *y, *rev, color, tag)) {
				*xl = *x; *yl = *y; *revl = *rev;
			}
			else *y = *y - GRID_LENGTH;
		}
		else {}
	}
}
int finddelete(int *del) {
	int flag = 0;
	for (int i = 1; i<16; i++) {
		int cnt = 0;
		for (int j = 1; j <= 10; j++) {
			if (getpixel(j * GRID_LENGTH + 5 * 3, (16 - i) * GRID_LENGTH + 15 * 3) != LIGHTBLUE) {
				cnt++;
			}
		}
		if (cnt == 10) {
			del[i] = -1;
			flag = 1;
			for (int f = 5, l = 6; f>0; f--, l++) {
				myfloodfill(f * GRID_LENGTH + 5 * 3, (16 - i) * GRID_LENGTH + 15 * 3, LIGHTBLUE);
				myfloodfill(l * GRID_LENGTH + 5 * 3, (16 - i) * GRID_LENGTH + 15 * 3, LIGHTBLUE);
			}
		}
		else if (cnt != 0) {
			int t = 0;
			for (int k = 1; k<i; k++) {
				if (del[k] == -1) t++;
			}
			del[i] = t;
		}
		else break;
	}
	return flag;
}
int moveline(int *del) {
	int sumofdelete = 0;
	for (int i = 1; i<16; i++) {
		if (del[i]>0) {
			for (int f = 5, l = 6; f>0; f--, l++) {
				myfloodfill(f * GRID_LENGTH + 5 * 3, (16 - (i - del[i])) * GRID_LENGTH + 15 * 3, getpixel(f * GRID_LENGTH + 5 * 3, (16 - i) * GRID_LENGTH + 15 * 3));
				myfloodfill(l * GRID_LENGTH + 5 * 3, (16 - (i - del[i])) * GRID_LENGTH + 15 * 3, getpixel(l * GRID_LENGTH + 5 * 3, (16 - i) * GRID_LENGTH + 15 * 3));
				myfloodfill(f * GRID_LENGTH + 5 * 3, (16 - i) * GRID_LENGTH + 15 * 3, LIGHTBLUE);
				myfloodfill(l * GRID_LENGTH + 5 * 3, (16 - i) * GRID_LENGTH + 15 * 3, LIGHTBLUE);
			}
		}
		if (del[i] == -1) sumofdelete++;
	}
	return sumofdelete;
}
int showselection() {
	setfillcolor(LIGHTGRAY);
	solidrectangle(5 * 3 + 20 * 3 - 5 * 3, 15 * 3 + 50 * 3 - 5 * 3, 5 * 3 + 130 * 3 + 5 * 3, 15 * 3 + 100 * 3 + 5 * 3);
	setfillcolor(BLUE);
	solidrectangle(5 * 3 + 20 * 3 - 5 * 3 + 2 * 5, 15 * 3 + 50 * 3 - 5 * 3 + 2 * 5, 5 * 3 + 130 * 3 + 5 * 3 - 2 * 5, 15 * 3 + 100 * 3 + 5 * 3 - 2 * 5);
	settextstyle(18, 0, L"黑体");
	outtextxy(5 * 3 + 20 * 3, 15 * 3 + 60 * 3, L"Game over! Do you want to play again?");
	outtextxy(5 * 3 + 60 * 3 - 5 * 3, 15 * 3 + 80 * 3 + 5 * 3, L"y");
	outtextxy(5 * 3 + 90 * 3 + 5 * 3, 15 * 3 + 80 * 3 + 5 * 3, L"n");
	char input;
	while (1) {
		if (_kbhit()) {
			input = _getch();
			if (input == 'y' || input == 'Y') return 1;
			else if (input == 'n' || input == 'N') return 0;
			else {}
		}
	}
}
